package com.ywyself.ext.utils;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.QuadCurve2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;

import javax.imageio.ImageIO;

/**
 * 表单验证码
 * 
 * @author ywyself
 * @createdate 2017-08-30 16:43:00
 */
public class YsVerifyUtils {
	/**
	 * 纯数字
	 */
	private static final String _SMSCode = "0123456789";
	/**
	 * 字母+数字，去掉易混淆的字符，（严格，去掉的较多）
	 */
	private static final String _CaptchaCode = "346789ABDEFHJKMNPQRTUWXYabdefghjkmnpqrstuwxy";
	/**
	 * 字母+数字，去掉易混淆的字符，（去掉1、0之类的）
	 */
	private static final String _STR = "23456789ABCDEFGHJKMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz";
	/**
	 * 获取随机数
	 */
	private static final Random RANDOM = new Random();

	/**
	 * 用给定字符串中的字符随机组合出给定长度的随机串
	 * 
	 * @param str
	 *            给定的字符串
	 * @param length
	 *            随机串的长度
	 * @return
	 */
	private static String getRandomString(String str, int length) {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < length; i++) {
			sb.append(str.charAt(RANDOM.nextInt(str.length())));
		}
		return sb.toString();
	}

	/**
	 * 获取数字组成的随机字符串，指定长度
	 * 
	 * @param length
	 *            字符串的长度
	 * @return 随机字符数字
	 */
	public static String getVerifyCode(int length) {
		return getRandomString(_SMSCode, length);
	}

	/**
	 * 获取数字组成的随机字符串，6位
	 * 
	 * @return 随机字符数字，6位
	 */
	public static String getVerifyCode() {
		return getVerifyCode(6);
	}

	/**
	 * 获取一个由字母和数字组成随机字符串
	 * 
	 * @param length
	 *            字符串的长度
	 * @return 随机字符串
	 */
	public static String getRandomString(int length) {
		return getRandomString(_STR, length);
	}

	/**
	 * 获取一个由字母和数字组成随机字符串，12位
	 * 
	 * @return 随机字符串，12位
	 */
	public static String getRandomString() {
		return getRandomString(12);
	}

	/**
	 * 获取一个图形验证码，由字母和数字组成，去掉可能混淆的字符与数字
	 * 
	 * @param length
	 *            验证码位数
	 * @return 验证码用的字符
	 */
	public static String getCaptchaCode(int length) {
		return getRandomString(_CaptchaCode, length);
	}

	/**
	 * 获取一个图形验证码，由字母和数字组成，去掉可能混淆的字符与数字，4位
	 * 
	 * @return 验证码用的字符，4位
	 */
	public static String getCaptchaCode() {
		return getRandomString(_CaptchaCode, 4);
	}

	/**
	 * 返回一张验证码流
	 * 
	 * @param captchaCode
	 *            验证码字符串
	 * @param width
	 *            宽度
	 * @param height
	 *            高度
	 * @param outputStream
	 * @throws IOException
	 */
	public static void getCaptchaStream(String captchaCode, int height, OutputStream outputStream) throws IOException {
		int codeLength = captchaCode.length();
		int width = height * codeLength * 2 / 3;
		//
		int charSize = 20; // 背景字母数量
		int maxLength = 10; // 线条水平垂直最大距离
		int lineSize = 10; // 线条的数量
		float noiseRate = 0.0f;// 噪声比例
		// 创建bufferedImage对象
		BufferedImage bImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		// 获取一只画笔
		Graphics2D g2 = bImage.createGraphics();

		g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
		// 图形抗锯齿
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		// 字体抗锯齿
		g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

		// 第一次：设置画笔颜色，并绘制一个矩形
		g2.setColor(getRandomColor(230, 255));// 设置背景色
		g2.fillRect(0, 0, width, height);
		// 绘制干扰线
		for (int i = 0; i < lineSize; i++) {
			int x1 = RANDOM.nextInt(width - maxLength);
			int y1 = RANDOM.nextInt(height - maxLength);
			int x2 = RANDOM.nextInt(maxLength) + x1;
			int y2 = RANDOM.nextInt(maxLength) + y1;
			g2.setColor(getRandomColor(100, 155));// 设置线条的颜色
			g2.drawLine(x1, y1, x2, y2);
		}
		// 添加噪点
		int area = (int) (noiseRate * width * height);
		for (int i = 0; i < area; i++) {
			int x = RANDOM.nextInt(width);
			int y = RANDOM.nextInt(height);
			int rgb = getRandomIntColor();
			bImage.setRGB(x, y, rgb);// 添加噪点
		}
		// 绘制小字符背景
		for (int i = 0; i < charSize; i++) {
			int degree = RANDOM.nextInt(45);
			// 旋转区域
			g2.rotate(Math.toRadians(degree));
			g2.setColor(getRandomColor(120, 200));
			String rand = String.valueOf(_STR.toCharArray()[RANDOM.nextInt(_STR.length())]);
			g2.drawString(rand, RANDOM.nextInt(width), RANDOM.nextInt(height));
			g2.rotate(-Math.toRadians(degree));
			// 旋转之后，必须旋转回来
		}
		// 绘制字符串
		int fontSize = height;
		// Font font = new Font("zapfino Forte LT Pro", Font.PLAIN, fontSize);
		Font font = null;
		String[] fontStyle = { "nyala", "Arial", "Bell MT", "Credit valley", "Impact" };
		char[] chars = captchaCode.toCharArray();
		AffineTransform affine = new AffineTransform();
		for (int i = 0; i < codeLength; i++) {
			// 字体
			font = new Font(fontStyle[RANDOM.nextInt(fontStyle.length)], Font.PLAIN, fontSize - 2 + RANDOM.nextInt(4));
			g2.setFont(font);
			// 旋转角度
			affine.setToRotation(Math.PI / 8 * RANDOM.nextDouble() * (RANDOM.nextBoolean() ? 1 : -1),
					(width / codeLength) * (i + 1 / 2), height / 2);
			g2.setTransform(affine);
			g2.setColor(getRandomColor(100, 130));
			g2.drawChars(chars, i, 1, ((width - 8) / codeLength) * i + 4, height / 2 + 11 + RANDOM.nextInt(2));
		}

		// 图片中间曲线，使用上面缓存的color
		g2.setColor(getRandomColor(120, 150));
		// width是线宽,float型
		BasicStroke bs = new BasicStroke(2);
		g2.setStroke(bs);
		// 画出曲线
		QuadCurve2D.Double curve = new QuadCurve2D.Double(0, RANDOM.nextDouble() * height, width / 2, height / 2, width,
				RANDOM.nextDouble() * height);
		g2.draw(curve);
		// 销毁图像
		g2.dispose();
		ImageIO.write(bImage, "jpg", outputStream);
	}

	/**
	 * 随机生成一种颜色，rgb取值
	 * 
	 * @param rangeFrom
	 *            rgb最低值
	 * @param rangeTo
	 *            rgb最高值
	 * @return 一种颜色
	 */
	private static Color getRandomColor(int rangeFrom, int rangeTo) {
		// 如果颜色设置越界
		if (rangeTo > 255) {
			rangeTo = 255;
		}
		// 如果范围选取不当
		if (rangeFrom > rangeTo) {
			rangeFrom = rangeTo;
		}
		//
		int r = rangeFrom + RANDOM.nextInt(rangeTo - rangeFrom);
		int g = rangeFrom + RANDOM.nextInt(rangeTo - rangeFrom);
		int b = rangeFrom + RANDOM.nextInt(rangeTo - rangeFrom);
		return new Color(r, g, b);
	}

	private static int getRandomIntColor() {
		int[] rgb = new int[3];
		for (int i = 0; i < 3; i++) {
			rgb[i] = RANDOM.nextInt(255);
		}
		int color = 0;
		for (int c : rgb) {
			color = color << 8;
			color = color | c;
		}
		return color;
	}

	/**
	 * 返回一张验证码图片
	 * 
	 * @param captchaCode
	 *            验证码字符串
	 * @param width
	 *            宽度
	 * @param height
	 *            高度
	 * @param outputFile
	 */
	public static void getCaptchaFile(String captchaCode, int height, File outputFile) {
		if (outputFile == null) {
			return;
		}
		try {
			outputFile.createNewFile();
			FileOutputStream fos = new FileOutputStream(outputFile);
			getCaptchaStream(captchaCode, height, fos);
			fos.close();
		} catch (IOException e) {
			e.getLocalizedMessage();
		}
	}

	public static void main(String[] args) {
		File file = new File("ver.jpg");
		getCaptchaFile(getCaptchaCode(), 120, file);
		System.out.println("finish");
	}

}
